package cn.zh.base.service.impl;


import cn.zh.base.constant.PhoneRegisterCons;
import cn.zh.base.dto.EmailRegisterDto;
import cn.zh.base.dto.PhoneRegisterDto;
import cn.zh.base.service.VerifyCodeService;
import cn.zh.base.util.BusinessException;
import cn.zh.base.util.SmsUtil;
import cn.zh.base.util.StrUtils;
import cn.zh.base.util.VerifyCodeUtils;
import cn.zh.user.domain.Logininfo;
import cn.zh.user.domain.User;
import cn.zh.user.mapper.LogininfoMapper;
import cn.zh.user.mapper.UserMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.io.IOException;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
@Service
@Slf4j
public class VerifyCodeServiceImpl implements VerifyCodeService {
    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private LogininfoMapper logininfoMapper;

    @Autowired
    private JavaMailSender javaMailSender;

    /**
     * 获取图形验证码
     *
     * @param imageCodeKey
     * @return
     */
    @Override
    public String imageCode(String imageCodeKey) throws IOException {
        if (StringUtils.isEmpty(imageCodeKey)){
            throw new BusinessException("请传递必要参数");
        }
        // 调用工具类方法获取四维随机数字符串
        String codeStr = VerifyCodeUtils.generateVerifyCode(1);
        // 将字符串设置到rides中，key就是传递进来的可以值，并设置过期时间五分钟
        redisTemplate.opsForValue().set(imageCodeKey,codeStr,3, TimeUnit.MINUTES);
        // 将code生成图形并编码成Base64
        return VerifyCodeUtils.VerifyCode(60,35,codeStr);
    }

    /**
     * 发送短信验吗
     *
     * @param dto
     */
    @Override
    public void smsCode(PhoneRegisterDto dto) {
        if (dto.getType().equals("binder")){
            this.sendSmsCode(dto,PhoneRegisterCons.BINDER_CODE_KEY);
        } else if (dto.getType().equals("register")) {
            verifyImageCode(dto);
            sendSmsCode(dto,PhoneRegisterCons.PHONE_CODE_KEY);
        }
    }



    private void sendSmsCode(PhoneRegisterDto dto, String redisKey) {
        // 判断手机号验证码两次发送时间大于60
        Long timeout = redisTemplate.getExpire(String.format(redisKey, dto.getPhone()));
        if (timeout > (PhoneRegisterCons.PHONE_CODE_TIME - 1*60L)) {
            throw new BusinessException("请在一分钟后再次申请");
        }
        // 是否需要重新生成手机号验证码
        // 基于我们目前Redis中有手机号码验证码，有就直接发，没有创建新的验证码
        String redisPhoneCode = (String)redisTemplate.opsForValue().get(String.format(redisKey, dto.getPhone()));
        if (StringUtils.isBlank(redisPhoneCode)) {
            redisPhoneCode = StrUtils.getRandomString(4);
        }
        log.info("手机验证码：{}",redisPhoneCode);
        // 将手机号验证码存到Redis
        redisTemplate.opsForValue().set(String.format(redisKey, dto.getPhone()),redisPhoneCode,
            PhoneRegisterCons.PHONE_CODE_TIME,TimeUnit.SECONDS);
        // 发送短信
//        SmsUtil.send(dto.getPhone(), redisPhoneCode);
    }

    private void verifyImageCode(PhoneRegisterDto dto) {
        //参数验证
        // 图形验证码是否正确
        // 从Redis中获取去腥验证阿妈然后和dto中的对比
        String redisImageCode = (String) redisTemplate.opsForValue().get(dto.getImageCodeKey());
        if (StringUtils.isBlank(redisImageCode) || !redisImageCode.equalsIgnoreCase(dto.getImageCodeValue())) {
            throw new BusinessException("图形码验证错误");
        }
        // 手机号是否注册过
        // 根据dto的手机号查询user表是否有数据
        User user = userMapper.findByPhone(dto.getPhone());
        if (user != null) {
            throw new BusinessException("该手机号已注册过，请直接登录");
        }
    }

    /**
     * 手机号注册接口
     * @param dto
     */
    @Override
    public void phoneRegister(PhoneRegisterDto dto) {
        // 参数校验
        // 验证两次密码是否一致
        if (!dto.getPassword().equals(dto.getPasswordRepeat())) {
            throw new BusinessException("密码不一致");
        }
        // 验证手机号码是否正确
        String redisPhoneCode = (String) redisTemplate.opsForValue().get(String.format(PhoneRegisterCons.PHONE_CODE_KEY, dto.getPhone()));
        if (StringUtils.isBlank(redisPhoneCode)  || !redisPhoneCode.equals(dto.getPhoneCode())) {
            throw new BusinessException("手机验证码错误");
        }
        // 验证手机号是否注册过
        User user = userMapper.findByPhone(dto.getPhone());
        if (user != null) {
            throw new BusinessException("该手机号已注册过，请直接登录");
        }
        // 保存数据 先 logininfo 再user
        user = dto.getUser();
        // 构建logininfo对象
        Logininfo logininfo = new Logininfo();
        BeanUtils.copyProperties(user,logininfo);
        logininfo.setDisable(true);
        logininfo.setType(1);
        logininfoMapper.add(logininfo);
        user.setLogininfoId(logininfo.getId());
        userMapper.add(user);
    }

    /**
     * 邮箱注册接口
     * @param dto
     */
    @Override
    public void emailRegister(EmailRegisterDto dto) {
// 参数校验
        // 判断两次密码是否一致
        if (!dto.getPassword().equals(dto.getPasswordRepeat())){
            throw new BusinessException("两次密码不一致！");
        }
        // 判断邮箱验证码是否正确
        String redisEmailCode = (String) redisTemplate.opsForValue().get(String.format("register:%s",dto.getEmail()));
        if (StringUtils.isBlank(redisEmailCode) || !redisEmailCode.equals(dto.getEmailCode())){
            throw new BusinessException("邮箱验证码错误！");
        }
        // 该邮箱是否注册过
        User user = userMapper.findByEmail(dto.getEmail());
        if (null != user) {
            throw new BusinessException("请登录！");
        }
        // 依次存进logininfo和user表里
        user = dto.getUser();
        // 构建logininfo对象
        Logininfo logininfo = new Logininfo();
        // 这个api 会将user对象中的字段值 复制到logininfo中 前提是两个实体字段相同
        BeanUtils.copyProperties(user,logininfo);
        logininfo.setDisable(true);
        logininfo.setType(1);
        // 添加logininfo表
        logininfoMapper.add(logininfo);
        user.setLogininfoId(logininfo.getId());
        userMapper.add(user);
    }

    /**
     * 发送邮箱验证码
     * @param dto
     */
    @Override
    public void emailCode(EmailRegisterDto dto) {
        verifyImageCode(dto);
        sendSmsCode(dto);
    }

    private void sendSmsCode(EmailRegisterDto dto) {
        // 4.判断邮箱验证码两次发送时间是否大于60秒
        Long  timeout =  redisTemplate.getExpire(String.format("register:%s", dto.getEmail()));
        if (timeout > 240L){
            throw new BusinessException("时间未到，莫点！");
        }
        // 5.是否需要重新生成邮箱验证码
        String redisEmailCode = (String) redisTemplate.opsForValue().get(String.format("register:%s", dto.getEmail()));
        if (StringUtils.isBlank(redisEmailCode)){
            // 如果没有 我们就生成一个
            redisEmailCode = StrUtils.getRandomString(4);
        }
        log.info("邮箱验证码：{}",redisEmailCode);

        // 6.将邮箱，验证码存到rides
        redisTemplate.opsForValue().set(String.format("register:%s", dto.getEmail()),redisEmailCode,
                300L,TimeUnit.SECONDS);
        // 7.发送邮箱
        try {
            MimeMessage mimeMessage = javaMailSender.createMimeMessage();
            MimeMessageHelper helper = new MimeMessageHelper(mimeMessage,true,"utf-8");
            helper.setFrom("573567075@qq.com");
            helper.setSubject("宠物乐园验证码通知");
            // 3.设置文本内容，第二个参数 true 代表是否是一个html内容
            helper.setText("<h1>注册验证码通知</h1>"+ "您注册的验证码是" +
                    redisEmailCode + ",请在三分钟内完成注册",true);
            // 4.添加附件
//            helper.addAttachment("好东西.jpg",new File("C:\\Users\\admin\\Desktop\\1.jpg"));
            // 5.设置收件人
            helper.setTo("573567075@qq.com");
            // 6.发送邮件
            javaMailSender.send(mimeMessage);
        } catch (MessagingException e) {
            e.printStackTrace();
        }
    }

    private void verifyImageCode(EmailRegisterDto dto) {
        // 1.校验参数
        // 2.判断图形验证码是否正确
//        通过key获取redis里的图形验证码
        String imageCodeValue = (String)redisTemplate.opsForValue().get(dto.getImageCodeKey());
//        如果图形验证码为空，或者图形验证码不正确，则抛出异常
        if (StringUtils.isBlank(imageCodeValue) || !imageCodeValue.equalsIgnoreCase(dto.getImageCodeValue())){
            throw new BusinessException("图形验证码错误！");
        }
        // 3.邮箱是否注册过
//        通过findByPhone方法找到指定的电话号码
        User user = userMapper.findByEmail(dto.getEmail());
        if(null != user){
            throw new BusinessException("请登录！");
        }
    }
}
